package com.terrace.core.utils.reflect;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.commons.lang.StringUtils;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.terrace.core.exception.ReflectCustomException;

/**
 * 对象属性帮助类
 * @author jiangyg
 *
 */
@SuppressWarnings("unchecked")
public class ObjectPropertyHelper {
	
	/**
	 * 要处理的Class
	 */
	private Class<?> cls;
	
	/**
	 * 是否忽略JsonIgnore注解的属性
	 */
	private boolean jsonIgnore = false;
	
	/**
	 * 有get、set方法的属性集合
	 */
	private List<Field> fields;
	
	/**
	 * 可生成json，且有get、set方法的属性名称集合
	 */
	private List<String> fieldNames;
	
	/**
	 * get方法集合
	 */
	private Map<String, Method> getMethods;
	
	/**
	 * set方法集合
	 */
	private Map<String, Method> setMethods;

	public ObjectPropertyHelper(Class<?> cls) {
		super();
		this.cls = cls;
		initFieldMethodsMap();
	}
	
	public ObjectPropertyHelper(Class<?> cls, boolean jsonIgnore) {
		super();
		this.cls = cls;
		this.jsonIgnore = jsonIgnore;
		initFieldMethodsMap();
	}

	/**
	 * 获取类的属性名称集合
	 * @return
	 */
	public List<String> getFieldNames() {
		return fieldNames;
	}

	/**
	 * 有get、set方法的属性集合
	 * @return
	 */
	public List<Field> getFields() {
		return fields;
	}
	
	public <A extends Annotation> A getAnnotation(Field field, Class<A> cls) {
		A ta = field.getAnnotation(cls);
		if (ta != null) {
			return ta;
		}
		
		Method getMethod = this.getMethods.get(field.getName());
		ta = getMethod.getAnnotation(cls);
		if (ta != null) {
			return ta;
		}
		
		Method setMethod = this.setMethods.get(field.getName());
		ta = setMethod.getAnnotation(cls);
		if (ta != null) {
			return ta;
		}
		return null;
	}

	/**
	 * 执行带参数的get方法
	 * @param property
	 * @param obj
	 * @param args
	 * @return
	 * @throws Exception
	 */
	public <T> T invokeGetMothod(String property, Object obj, Object... args) throws Exception {
		Method m = getMethods.get(property);
		if (m != null) {
			return (T) m.invoke(obj, args);
		} else {
			throw new ReflectCustomException("没有找到属性[" + property + "]对应的get方法");
		}
	}
	
	/**
	 * 执行带参数的set方法
	 * @param property
	 * @param obj
	 * @param args
	 * @throws Exception
	 */
	public void invokeSetMothod(String property, Object obj, Object... args) throws Exception {
		Method m = setMethods.get(property);
		if (m != null) {
			m.invoke(obj, args);
		} else {
			throw new ReflectCustomException("没有找到属性[" + property + "]对应的set方法");
		}
	}
	
	/**
	 * 初始化字段名称和对用的get、set方法
	 */
	private void initFieldMethodsMap() {
		Method[] methods = this.cls.getMethods();
		Field[] fs = cls.getDeclaredFields();
		
		// 初始化集合长度
		int length = fs.length + 1;
		fields = new ArrayList<Field>(length);
		fieldNames = new ArrayList<String>(length);
		getMethods = new ConcurrentHashMap<String, Method>(length);
		setMethods = new ConcurrentHashMap<String, Method>(length);
		
		for (Field field : fs) {
			String fieldName = field.getName();
			
			boolean getFlag = false, setFlag = false;
			
			for (Method method : methods) {
				
				String methodName = method.getName();
				
				// get方法
				if (StringUtils.equalsIgnoreCase(methodName, "get" + fieldName)
						|| StringUtils.equalsIgnoreCase(methodName, "is" + fieldName)) {
					getMethods.put(fieldName, method);
					getFlag = true;
					continue;
				}
				
				// set方法
				if (StringUtils.equalsIgnoreCase(methodName, "set" + fieldName)) {
					setMethods.put(fieldName, method);
					setFlag = true;
					continue;
				}
				
				// set、get方法齐全，则退出当前字段的循环
				if (getFlag && setFlag) {
					
					// 有get方法的属性，增加到属性集合中
					if (!jsonIgnore || (jsonIgnore && !isIgnoreProperty(getMethods.get(fieldName))
							&& !isIgnoreProperty(setMethods.get(fieldName)))) {
						fieldNames.add(fieldName);
						fields.add(field);
					}
					
					break;
				}
				
			}
		}
	}
	
	/**
	 * 是否是需要忽略的方法--有JsonIgnore注解的忽略
	 * @param method
	 * @return
	 */
	private boolean isIgnoreProperty(Method method) {
		Annotation[] annotations = method.getAnnotations();
		for (Annotation annotation : annotations) {
			if (annotation instanceof JsonIgnore) {
				return true;
			}
		}
		return false;
	}
	
}
